package com.trinity.orders.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.v3.util.SignUtils;
import com.google.gson.Gson;
import com.trinity.common.core.domain.AjaxResult;
import com.trinity.common.core.domain.model.LoginUser;
import com.trinity.common.core.redis.RedisCache;
import com.trinity.common.exception.ServiceException;
import com.trinity.common.exception.order.OrderAlreadyPaidException;
import com.trinity.common.exception.order.OrderCreationFailedException;
import com.trinity.common.exception.order.OrderNotExistsException;
import com.trinity.common.exception.order.QRCodeGenerationFailedException;
import com.trinity.common.utils.DateUtils;
import com.trinity.common.utils.SecurityUtils;
import com.trinity.common.utils.StringUtils;
import com.trinity.common.utils.bean.BeanUtils;
import com.trinity.config.WxPayProperties;
import com.trinity.order.refund.domain.XcRefundOrder;
import com.trinity.order.refund.service.IXcRefundOrderService;
import com.trinity.orders.constant.OrderConstants;
import com.trinity.orders.domain.XcOrders;
import com.trinity.orders.domain.XcOrdersGoods;
import com.trinity.orders.domain.XcPayRecord;
import com.trinity.orders.dto.AddManualOrderDto;
import com.trinity.orders.dto.AddOrderDto;
import com.trinity.orders.dto.PayRecordDto;
import com.trinity.orders.mapper.XcOrdersGoodsMapper;
import com.trinity.orders.mapper.XcOrdersMapper;
import com.trinity.orders.mapper.XcPayRecordMapper;
import com.trinity.orders.service.IXcOrdersService;
import com.trinity.orders.service.IXcPayRecordService;
import com.trinity.orders.utils.IdWorker;
import com.trinity.orders.utils.QRCodeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 订单信息Service业务层处理
 *
 * @author trinity
 * @date 2023-11-19
 */
@Service
@Slf4j
public class XcOrdersServiceImpl extends ServiceImpl<XcOrdersMapper, XcOrders> implements IXcOrdersService {

    @Resource
    private IdWorker idWorker;

    @Resource
    XcOrdersMapper xcOrdersMapper;

    @Resource
    XcOrdersGoodsMapper ordersGoodsMapper;
    @Resource
    XcPayRecordMapper payRecordMapper;

    @Resource
    private WxPayService wxService;

    @Resource
    private WxPayProperties properties;


    @Resource
    private IXcOrdersService ordersService;

    @Resource
    private IXcPayRecordService payRecordService;

    @Resource
    private IXcRefundOrderService refundOrderService;

    private Gson gson = new Gson();

    @Autowired
    private RedisCache redisCache;

    /**
     * 改变缴费项目关联账单的状态
     */
    @Override
    public void changeStatus(String listId, String status) {
        xcOrdersMapper.update(null, new LambdaUpdateWrapper<XcOrders>()
                .set(XcOrders::getOther2, status)
                .eq(XcOrders::getOther1, listId));
    }

    /**
     * 查询订单信息
     *
     * @param id 订单信息主键
     * @return 订单信息
     */
    @Override
    public XcOrders selectXcOrdersById(String id) {
        return xcOrdersMapper.selectXcOrdersById(id);
    }

    @Override
    @Transactional
    public AjaxResult requestPayment(String id, String openid) throws WxPayException {
        Map<String, String> jsapiResultMap = new HashMap<>();

        //1 添加商品订单
        XcOrders order = ordersService.selectXcOrdersById(id);
        if (order == null) {
            throw new ServiceException("订单不存在");
        }

        if (order.getStatus().equals(OrderConstants.TradeState.SUCCESS)) {
            // 订单已支付
            throw new OrderAlreadyPaidException();
        }
        //有成功的订单
        XcPayRecord payRecord = payRecordService.selectPayRecordSuccessByOrderId(id);
        if (payRecord != null &&!StringUtils.isEmpty(payRecord.getPrepayId())&&OrderConstants.TradeState.SUCCESS.equals(payRecord.getStatus())){
            // 订单已支付
            throw new OrderAlreadyPaidException();
        }
        if (order.getStatus().equals(OrderConstants.TradeState.USERPAYING)) {
            // 正在支付中
            throw new ServiceException("订单正在支付中");
        }
        //添加支付交易记录 获取微支付德订单
        payRecord = payRecordService.selectPayRecordNoPayByOrderId(id);

        if (payRecord != null && !StringUtils.isEmpty(payRecord.getPrepayId())) {
            JSONObject jsonObject = JSONObject.parseObject(payRecord.getPrepayId());
            String appId = jsonObject.getString("appId");
            String packageValue = jsonObject.getString("packageValue");
            String nonceStr = SignUtils.genRandomStr();
            String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
            String signStr = String.format("%s\n%s\n%s\n%s\n", appId, timestamp, nonceStr, packageValue);
            //掉这个方法才会获得PrivateKey
            CloseableHttpClient closeableHttpClient = wxService.getConfig().initApiV3HttpClient();
//            wxPayConfiguration.wxService().getConfig();
            String sign = SignUtils.sign(signStr, wxService.getConfig().getPrivateKey());
            jsapiResultMap.put("appId", appId);
            jsapiResultMap.put("timeStamp", timestamp);
            jsapiResultMap.put("nonceStr", nonceStr);
            jsapiResultMap.put("packageValue", packageValue);
            jsapiResultMap.put("signType", jsonObject.getString("signType"));
            jsapiResultMap.put("paySign", sign);
            jsapiResultMap.put("payNo", payRecord.getPayNo());
            return AjaxResult.success(jsapiResultMap);
        }
        try {
            //创建支付订单
            payRecord = createWxOrderPayRecord(order, openid);
            int i = payRecordMapper.insertXcPayRecord(payRecord);
            if (i <= 0) {
                //插入记录失败
                throw new ServiceException("插入记录失败");
            }
            //微信下单 - 没有记录
            WxPayUnifiedOrderV3Result.JsapiResult jsapiResult = wxGetPament(payRecord);
            String packageValue = jsapiResult.getPackageValue();
            jsapiResultMap.put("appId", jsapiResult.getAppId());
            jsapiResultMap.put("timeStamp", jsapiResult.getTimeStamp());
            jsapiResultMap.put("nonceStr", jsapiResult.getNonceStr());
            jsapiResultMap.put("packageValue", packageValue);
            jsapiResultMap.put("signType", jsapiResult.getSignType());
            jsapiResultMap.put("paySign", jsapiResult.getPaySign());
            jsapiResultMap.put("payNo", payRecord.getPayNo());
            //保存支付密钥
            payRecord.setPrepayId(saveJsApiId(jsapiResultMap));
            payRecord.setOpenid(openid);
            payRecordMapper.updateXcPayRecord(payRecord);
        } catch (WxPayException e) {
            log.error("下单失败： {}", e.getErrCode());
            payRecord.setStatus(OrderConstants.TradeState.PAYERROR);
            payRecord.setRemark(e.getErrCodeDes());
            payRecordMapper.updateXcPayRecord(payRecord);
            return AjaxResult.error(e.getCustomErrorMsg());
        } catch (ServiceException e) {
            log.error("下单失败： {}", e.getCode());
            return AjaxResult.error(e.getMessage());
        } finally {
//            queryOrder(id);
        }
        return AjaxResult.success(jsapiResultMap);
    }
    @Override
    @Transactional
    public AjaxResult requestPaymentV2(String id, String openid) throws WxPayException {

        // 获取支付参数
        XcOrders order = validateOrder(id);

        // 根据订单信息获取支付记录
        XcPayRecord payRecord = getPayRecord(order);

        // 检查支付记录是否存在且预支付ID不为空
        if (payRecord != null && !org.apache.commons.lang3.StringUtils.isEmpty(payRecord.getPrepayId())) {
            // 如果预支付订单有效
            if (isPrepayOrderValid(payRecord)) {
                // 从现有支付记录构建支付结果并返回
                return buildPaymentResultFromExistingRecord(payRecord);
            }
        }
        // 如果预支付订单无效，重新下单
        return createAndProcessNewPayment(order, openid, payRecord);
    }

    /**
     * 验证订单是否存在 并且处于未支付状态
     *
     * @param id
     * @return
     */
    private XcOrders validateOrder(String id) {
        XcOrders order = ordersService.selectXcOrdersById(id);
        if (order == null) {
            throw new ServiceException("订单不存在");
        }
        if (order.getStatus().equals(OrderConstants.TradeState.SUCCESS)) {
            throw new OrderAlreadyPaidException();
        }
        if (order.getStatus().equals(OrderConstants.TradeState.USERPAYING)) {
            throw new ServiceException("订单正在支付中");
        }
        return order;
    }

    /**
     * 获取未支付的支付记录
     *
     * @param order
     * @return
     */
    private XcPayRecord getPayRecord(XcOrders order) {
        XcPayRecord payRecord = payRecordService.selectPayRecordSuccessByOrderId(order.getId());
        if (payRecord != null && !org.apache.commons.lang3.StringUtils.isEmpty(payRecord.getPrepayId()) && OrderConstants.TradeState.SUCCESS.equals(payRecord.getStatus())) {
            throw new OrderAlreadyPaidException();
        }
        return payRecordService.selectPayRecordNoPayByOrderId(order.getId());
    }

    /**
     * 是否有效
     *
     * @param payRecord
     * @return
     */
    private boolean isPrepayOrderValid(XcPayRecord payRecord) {
        String redisKey = OrderConstants.PAY_RECORD_KEY + payRecord.getPayNo();
        return redisCache.hasKey(redisKey);
    }

    /**
     * 构建支付结果
     *
     * @param payRecord
     * @return
     */
    private AjaxResult buildPaymentResultFromExistingRecord(XcPayRecord payRecord) {
        Map<String, String> jsapiResultMap = new HashMap<>();
        JSONObject jsonObject = JSONObject.parseObject(payRecord.getPrepayId());
        String appId = jsonObject.getString("appId");
        String packageValue = jsonObject.getString("packageValue");
        String nonceStr = SignUtils.genRandomStr();
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String signStr = String.format("%s\n%s\n%s\n%s\n", appId, timestamp, nonceStr, packageValue);
        String sign = SignUtils.sign(signStr, wxService.getConfig().getPrivateKey());
        jsapiResultMap.put("appId", appId);
        jsapiResultMap.put("timeStamp", timestamp);
        jsapiResultMap.put("nonceStr", nonceStr);
        jsapiResultMap.put("packageValue", packageValue);
        jsapiResultMap.put("signType", jsonObject.getString("signType"));
        jsapiResultMap.put("paySign", sign);
        jsapiResultMap.put("payNo", payRecord.getPayNo());
        return AjaxResult.success(jsapiResultMap);
    }

    /**
     * 创建新的支付记录
     * @param order
     * @param openid
     * @param payRecord
     * @return
     * @throws WxPayException
     */
    private AjaxResult createAndProcessNewPayment(XcOrders order, String openid, XcPayRecord payRecord) {
        Map<String, String> jsapiResultMap = new HashMap<>();
        try {

            //首次创建支付订单
            if (payRecord == null || org.apache.commons.lang3.StringUtils.isEmpty(payRecord.getPrepayId())) {
                payRecord = createWxOrderPayRecord(order, openid);
                int insertResult = payRecordMapper.insertXcPayRecord(payRecord);
                if (insertResult <= 0) {
                    throw new ServiceException("插入记录失败");
                }
            }
            WxPayUnifiedOrderV3Result.JsapiResult jsapiResult = wxGetPament(payRecord);
            String packageValue = jsapiResult.getPackageValue();
            jsapiResultMap.put("appId", jsapiResult.getAppId());
            jsapiResultMap.put("timeStamp", jsapiResult.getTimeStamp());
            jsapiResultMap.put("nonceStr", jsapiResult.getNonceStr());
            jsapiResultMap.put("packageValue", packageValue);
            jsapiResultMap.put("signType", jsapiResult.getSignType());
            jsapiResultMap.put("paySign", jsapiResult.getPaySign());
            jsapiResultMap.put("payNo", payRecord.getPayNo());
            payRecord.setPrepayId(saveJsApiId(jsapiResultMap));
            payRecord.setOpenid(openid);
            //更新订单的预支付 payNo
            payRecordMapper.updateXcPayRecord(payRecord);

            String redisKey = OrderConstants.PAY_RECORD_KEY + payRecord.getPayNo();
            redisCache.setCacheObject(redisKey, payRecord, 1, TimeUnit.MINUTES);

        } catch (WxPayException e) {
            log.error("下单失败： {}", e.getErrCode());
            payRecord.setStatus(OrderConstants.TradeState.PAYERROR);
            payRecord.setRemark(e.getErrCodeDes());
            payRecordMapper.updateXcPayRecord(payRecord);
            return AjaxResult.error(e.getCustomErrorMsg());
        } catch (ServiceException e) {
            log.error("下单失败： {}", e.getCode());
            return AjaxResult.error(e.getMessage());
        }
        return AjaxResult.success(jsapiResultMap);
    }

    //请求生成预支付订单

    public String saveJsApiId(Map<String, String> jsapiResultMap) {
        // 选择需要的键
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("appId", jsapiResultMap.get("appId"));
        jsonObject.put("nonceStr", jsapiResultMap.get("nonceStr"));
        jsonObject.put("packageValue", jsapiResultMap.get("packageValue"));
        jsonObject.put("signType", jsapiResultMap.get("signType"));
        return jsonObject.toString();
    }

    @Override
    public AjaxResult pollingOrder(String orderNo, String payNo, String tradeState) {
        WxPayOrderQueryV3Result result;
        try {
            XcPayRecord payRecord = queryPayRecordByOrderId(orderNo, payNo);
            orderNo = payRecord.getOrderId();
//            if (OrderConstants.TradeState.CENCEL.equals(tradeState)) {
//                payRecord.setStatus(OrderConstants.TradeState.CENCEL);
//                wxService.closeOrderV3(payNo);
//                int i = payRecordService.updateXcPayRecord(payRecord);
//                return AjaxResult.error("取消支付!");
//            }
            result = wxService.queryOrderV3("", payNo);
            payRecord.setOutPayNo(result.getTransactionId());
            payRecord.setPayMethods(result.getTradeType());
            payRecord.setStatus(result.getTradeState());
            String timeEnd = result.getSuccessTime();
            payRecord.setPaySuccessTime(timeEnd);
            XcOrders xcOrders = ordersService.selectXcOrdersById(orderNo);
            xcOrders.setStatus(result.getTradeState());
            int i1 = ordersService.updateXcOrders(xcOrders);
            int i = payRecordService.updateXcPayRecord(payRecord);
        } catch (WxPayException e) {
            return AjaxResult.error(e.getCustomErrorMsg());
        }
        return AjaxResult.success(result);
    }

    /**
     * 未使用
     *
     * @param orderNo
     */
    public void queryOrder(String orderNo) {
        XcPayRecord xcPayRecord = new XcPayRecord();
        xcPayRecord.setOrderId(orderNo);
        List<XcPayRecord> payRecordList = payRecordService.selectXcPayRecordList(xcPayRecord);
        for (XcPayRecord payRecord : payRecordList) {
            WxPayOrderQueryResult result;
            try {
                String payNo = payRecord.getPayNo();
                result = wxService.queryOrder("", payNo);
                payRecord.setOutPayNo(result.getTransactionId());
                payRecord.setPayMethods(result.getTradeType());
                payRecord.setStatus(result.getTradeState());
                String timeEnd = result.getTimeEnd();
                payRecord.setPaySuccessTime(timeEnd);
                //交易成功 更新订单状态 锁定该流水，其余成功订单退款处理
                if (OrderConstants.TradeState.SUCCESS.equals(result.getTradeState())) {
                    XcOrders xcOrders = ordersService.selectXcOrdersById(orderNo);
                    xcOrders.setStatus(result.getTradeState());
                    //记录流水号
                    xcOrders.setOther2(result.getTransactionId());
                    int i1 = ordersService.updateXcOrders(xcOrders);
                }
                int i = payRecordService.updateXcPayRecord(payRecord);
            } catch (WxPayException ignored) {
                log.error("查询异常： {}", ignored.getErrCode());
            }
        }


    }

    public XcPayRecord queryPayRecordByOrderId(String orderId, String payNo) {
        XcPayRecord xcPayRecord = new XcPayRecord();
        xcPayRecord.setOrderId(orderId);
        xcPayRecord.setPayNo(payNo);
        List<XcPayRecord> payRecordList = payRecordService.selectXcPayRecordList(xcPayRecord);
        if (payRecordList != null) {
            return payRecordList.get(0);
        }
        return null;
    }

    /**
     * 查询订单信息列表
     *
     * @param xcOrders 订单信息
     * @return 订单信息
     */
    @Override
    public List<XcOrders> selectXcOrdersList(XcOrders xcOrders) {
        return xcOrdersMapper.selectXcOrdersList(xcOrders);
    }
    @Override
    public List<XcOrders> selectOrderIdSList(String[] ids) {
        return xcOrdersMapper.selectOrderIdSList(ids);
    }


    @Transactional
    @Override
    public PayRecordDto AppcreateOrder(String studentId, AddOrderDto addOrderDto) throws WxPayException {
        //添加商品订单
        XcOrders xcOrders = saveXcOrders(studentId, addOrderDto);
        //添加支付交易记录
        XcPayRecord payRecord = createWxOrderPayRecord(xcOrders, "");
        //微信下单
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(payRecordDto, payRecord);
        WxPayUnifiedOrderV3Result.JsapiResult jsapiResult = wxGetPament(payRecord);
        Map<String, String> jsapiResultMap = new HashMap<>();
        String packageValue = jsapiResult.getPackageValue();
        jsapiResultMap.put("appld", jsapiResult.getAppId());
        jsapiResultMap.put("timeStamp", jsapiResult.getTimeStamp());
        jsapiResultMap.put("nonceStr", jsapiResult.getNonceStr());
        jsapiResultMap.put("packageValue", packageValue);
        jsapiResultMap.put("signType", jsapiResult.getSignType());
        jsapiResultMap.put("paySign", jsapiResult.getPaySign());
        String prepayId = packageValue.substring(packageValue.indexOf("=") + 1);
        jsapiResultMap.put("prepayId", prepayId);
        jsapiResultMap.put("payNo", payRecord.getPayNo());
        payRecord.setStatus(OrderConstants.TradeState.USERPAYING);
        return payRecordDto;
    }

    //微信下单
    public WxPayUnifiedOrderV3Result.JsapiResult wxGetPament(XcPayRecord payRecord) throws WxPayException {
        WxPayUnifiedOrderV3Request v3 = new WxPayUnifiedOrderV3Request();
        v3.setDescription(payRecord.getOrderName());
        Date nowDate = new Date();
        Date dateAfter = new Date(nowDate.getTime() + 300000);
        String format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(dateAfter);
        v3.setTimeExpire(format);
        WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
//        double v = Double.parseDouble(payRecord.getTotalPrice());
//        Integer amountTotal = Math.toIntExact((long) (v * 100));
        Integer amountTotal = Integer.valueOf(payRecord.getTotalPrice());
        amount.setTotal(amountTotal);
        amount.setCurrency("CNY");
        v3.setAmount(amount);
        v3.setOutTradeNo(payRecord.getPayNo());
        v3.setAttach(payRecord.getOrderId());
//        v3.setNotifyUrl("https://dhxhsd.yuexinshao.cn/notify");
        WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
        payer.setOpenid(payRecord.getOpenid());
        v3.setPayer(payer);
        return wxService.createOrderV3(TradeTypeEnum.JSAPI, v3);

    }

    @Transactional
    @Override
    public String release(List<AddOrderDto> batch) {
        //1.生成账单
        int i = 0;
        for (AddOrderDto dto : batch) {
            XcOrders order = this.createOrder(dto.getCustom(), dto);
            i++;
        }
        Map<String, Object> result = new HashMap<>();
        result.put("complete", i);
        return result.toString();
    }

    @Transactional
    @Override
    public String revokeRelease(String businessId) {
        Map<String, Object> result = new HashMap<>();

        XcOrders order = new XcOrders();
        order.setOther1(businessId);
        List<XcOrders> xcOrders = xcOrdersMapper.selectXcOrdersList(order);
        if (xcOrders != null && xcOrders.size() > 0) {
            String[] orderIds = xcOrders.stream()
                    .map(XcOrders::getId)
                    .toArray(String[]::new);
            //先删除订单详情
            int j = ordersGoodsMapper.deleteXcOrdersGoodsByOderIds(orderIds);
            //删除订单
            int i = xcOrdersMapper.deleteXcOrdersByIds(orderIds);
            result.put("revokeOder", i);
            result.put("revokeOderGood", j);
        }
        return result.toString();
    }

    @Transactional
    @Override
    public String revokeReleaseOne(String listId, Long studentId) {
        if (listId == null || listId.isEmpty() || studentId == null) {
            return "参数错误";
        }
        String outBusinessId = listId + "-" + studentId;
        XcOrders orders = xcOrdersMapper.selectXcOrdersByBusinessId(outBusinessId);
        if (orders == null) {
            return "订单不存在";
        }
        String[] orderIds = {orders.getId()};
        if (OrderConstants.TradeState.NOTPAY.equals(orders.getStatus())) {
            try {
                // 先删除订单详情
                int j = ordersGoodsMapper.deleteXcOrdersGoodsByOderIds(orderIds);
                // 删除订单
                int i = xcOrdersMapper.deleteXcOrdersByIds(orderIds);
                // 可以添加对删除结果的检查
            } catch (Exception e) {
                // 异常会被事务管理器捕获并回滚
                throw new RuntimeException("订单删除失败", e);
            }
        } else {
            // 对于非NOTPAY状态的订单，应该有相应的处理逻辑
            // 这里暂时保持原逻辑，但实际应该根据业务需求完善
            return orders.getStatus()+"订单状态不支持撤回";
        }

        return "订单撤回成功";
    }

    @Transactional
    @Override
    public XcOrders createOrder(String studentId, AddOrderDto addOrderDto) {
        //添加商品订单
        XcOrders orders = saveXcOrders(studentId, addOrderDto);
        if (orders == null) {
//            XueChengPlusException.cast("订单创建失败");
            throw new OrderCreationFailedException();
        }
        if (OrderConstants.TradeState.SUCCESS.equals(orders.getStatus())) {
//            XueChengPlusException.cast("订单已支付");
            throw new OrderAlreadyPaidException();
        }
        return orders;
    }

    /**
     * 获取支付参数
     * @param order
     * @param openid
     * @return
     */

    @Transactional
    @Override
    public XcPayRecord createWxOrderPayRecord(XcOrders order, String openid) {
        XcPayRecord payRecord = new XcPayRecord();
        //生成支付交易流水号 传给第三方支付平台
        long payNo = idWorker.nextId();
        payRecord.setOrderId(String.valueOf(order.getId()));//商品订单号
        payRecord.setPayNo(String.valueOf(payNo));
        payRecord.setStudentId(order.getStudentId());
        payRecord.setStudentName(order.getStudent().getStudentName());
//        WeChart Alipay 银联 现金
        payRecord.setOutPayChannel("WeChart");
        payRecord.setSubPayChannel("微信小程序");
        payRecord.setPayMethods("小程序支付");
        payRecord.setOrderName(order.getOrderName());
        payRecord.setTotalPrice(order.getTotalPrice());
        payRecord.setCurrency("CNY");
        payRecord.setCreateTime(DateUtils.getNowDate());
        payRecord.setStatus(OrderConstants.TradeState.NOTPAY);//未支付
        payRecord.setOpenid(openid);
        //30分钟
//        payRecord.setExpireTime();
        return payRecord;
    }

    /**
     * 未使用
     *
     * @param userId
     * @param addOrderDto 订单信息
     * @return
     */
    @Transactional
    @Override
    public PayRecordDto createOrderAndPayRecord(String userId, AddOrderDto addOrderDto) {

        //添加商品订单
        XcOrders orders = saveXcOrders(userId, addOrderDto);
        if (orders == null) {
//            XueChengPlusException.cast("订单创建失败");
            throw new OrderCreationFailedException();
        }
        if ("SUCCESS".equals(orders.getStatus())) {
//            XueChengPlusException.cast("订单已支付");
            throw new OrderAlreadyPaidException();
        }

        //添加支付交易记录
        XcPayRecord payRecord = createPayRecord(orders);
        //生成二维码
//        String s = wxCreateOrder(payRecord);
        String s = "";
        String qrCode = null;
        try {
            String url = String.format(s, payRecord.getPayNo());
            qrCode = QRCodeUtil.createQRCode(url, 200, 200);
        } catch (IOException e) {
//            XueChengPlusException.cast("生成二维码出错");
            throw new QRCodeGenerationFailedException();
        }
        //支付记录
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(payRecordDto, payRecord);
        payRecordDto.setQrcode(qrCode);
        return payRecordDto;
    }

    /**
     * 未使用
     *
     * @param userId
     * @param manualOrderDto 手工单信息
     * @return
     */
    @Transactional
    @Override
    public PayRecordDto createManualOrder(String userId, AddManualOrderDto manualOrderDto) {
        AddOrderDto addOrderDto = new AddOrderDto();
        BeanUtils.copyBeanProp(addOrderDto, manualOrderDto);
        //添加商品订单
        XcOrders orders = saveManualOrders(userId, addOrderDto);
        if (orders == null) {
//            XueChengPlusException.cast("订单创建失败");
            throw new OrderCreationFailedException();
        }
        if ("SUCCESS".equals(orders.getStatus())) {
//            XueChengPlusException.cast("订单已支付");
            throw new OrderAlreadyPaidException();
        }

        //添加支付交易记录
        PayRecordDto payRecordDto = new PayRecordDto();
        payRecordDto.setId(orders.getId());
        payRecordDto.setPayNo(manualOrderDto.getManualOrderId());
        payRecordDto.setOrderId(manualOrderDto.getManualOrderId());//商品订单号
        payRecordDto.setOrderName(manualOrderDto.getOrderName());
        payRecordDto.setTotalPrice(manualOrderDto.getManualTotalPrice());
        payRecordDto.setOutPayNo(String.valueOf(manualOrderDto.getManualOrderId()));
        payRecordDto.setOutPayChannel("手工单");
        payRecordDto.setStatus("SUCCESS");
        payRecordDto.setCurrency("CNY");
//        payRecordDto.setCreateDate(DateUtils.getNowDate());
//        payRecordDto.setPaySuccessTime(DateUtils.getNowDate());
//        payRecordDto.setUserId(manualOrderDto.getManualUsername());
        return payRecordDto;
    }

    /**
     * 在保存订单接口中需要完成创建商品订单、创建支付交易记录，
     *
     * @param studentId
     * @param addOrderDto
     * @return
     */
    @Transactional
    public XcOrders saveXcOrders(String studentId, AddOrderDto addOrderDto) {
        //幂等性处理
        //进行幂等性判断，同一个缴费项目只能有一个订单 待缴费的订单
        XcOrders order = getOrderByBusinessId(addOrderDto.getOutBusinessId());
        if (order != null) {
            return order;
        } else {
            order = new XcOrders();
            //生成订单号
            long orderId = idWorker.nextId();
            order.setId(String.valueOf(orderId));
            order.setDeptId(addOrderDto.getDeptId());
            order.setTotalPrice(addOrderDto.getTotalPrice());
            order.setCreateTime(DateUtils.getNowDate());
            order.setUpdateTime(DateUtils.getNowDate());
            order.setCreateBy(SecurityUtils.getLoginUser().getUsername());
            //xc402_system tables dictionary
            order.setStatus(OrderConstants.TradeState.NOTPAY);//未支付
            order.setUserId(studentId);
            order.setStudentId(studentId);
            order.setOrderType(addOrderDto.getOrderType());
            order.setOrderName(addOrderDto.getOrderName());
            order.setOrderDetail(addOrderDto.getOrderDetail());
            order.setOrderDescrip(addOrderDto.getOrderDescription());
            //选课记录id
            order.setOutBusinessId(addOrderDto.getOutBusinessId());
            order.setOther1(addOrderDto.getBookListId());
            //创建时为进行中
            order.setOther2("2");
            int i = xcOrdersMapper.insertXcOrders(order);
            if (i <= 0) {
                //插入失败抛出异常
                throw new OrderCreationFailedException();
            }
            //个性化缴费有订单详情
            if (OrderConstants.PaymentType.UNIQUE.equals(order.getOrderType())) {
                //插入订单明细
                String orderDetailJson = addOrderDto.getOrderDetail();
                List<XcOrdersGoods> xcOrdersGoodsList = JSON.parseArray(orderDetailJson, XcOrdersGoods.class);
                xcOrdersGoodsList.forEach(goods -> {
                    goods.setOrderId(orderId);//订单号
                    ordersGoodsMapper.insertXcOrdersGoods(goods);
                });
            }
//
        }

        return order;
    }

    /**
     * 手工单
     *
     * @param userId
     * @param addOrderDto
     * @return
     */
    @Transactional
    public XcOrders saveManualOrders(String userId, AddOrderDto addOrderDto) {
        //幂等性处理
        //进行幂等性判断，同一个缴费项目只能有一个订单
        XcOrders order = getOrderByBusinessId(addOrderDto.getOutBusinessId());
        if (order != null) {
            order.setStatus("SUCCESS");//支付完成
            order.setOrderType("M");
            xcOrdersMapper.updateXcOrders(order);
            return order;
        }
        order = new XcOrders();
        //生成订单号
//        long orderId = IdWorkerUtils.getInstance().nextId();
        // 生成orderId
        long orderId = idWorker.nextId();
        order.setId(String.valueOf(orderId));
        order.setTotalPrice(addOrderDto.getTotalPrice());
        order.setCreateTime(DateUtils.getNowDate());
        order.setStatus("SUCCESS");//支付完成
        order.setUserId(userId);
        //手工单
        order.setOrderType("M");
        order.setOrderName(addOrderDto.getOrderName());
        order.setOrderDetail(addOrderDto.getOrderDetail());
        order.setOrderDescrip(addOrderDto.getOrderDescription());
        order.setOutBusinessId(addOrderDto.getOutBusinessId());//选课记录id
        int i = xcOrdersMapper.insertXcOrders(order);
        if (i <= 0) {
            //插入失败抛出异常
            throw new OrderCreationFailedException();
        }
        return order;
    }

    //根据业务id查询订单 选课记录表当中的主键
    @Override
    public XcOrders getOrderByBusinessId(String businessId) {
        return xcOrdersMapper.selectXcOrdersByBusinessId(businessId);
    }

    public XcOrders getNoPayOrderByBusinessId(String businessId) {
        return xcOrdersMapper.getOrderByBusinessIdAndState(businessId, OrderConstants.OrderState.NOTPAY);
    }

    //    创建支付交易记录
    public XcPayRecord createPayRecord(XcOrders orders) {
        XcOrders xcOrders = xcOrdersMapper.selectXcOrdersById(orders.getId());
        if (xcOrders == null) {
//            XueChengPlusException.cast("订单不存在");
            throw new OrderNotExistsException();
        }
        if (OrderConstants.TradeState.SUCCESS.equals(xcOrders.getStatus())) {
//            XueChengPlusException.cast("订单已支付");
            throw new OrderAlreadyPaidException();
        }
        XcPayRecord payRecord = new XcPayRecord();
        //生成支付交易流水号 传给第三方支付平台
        long payNo = idWorker.nextId();
        payRecord.setPayNo(String.valueOf(payNo));
        payRecord.setOrderId(xcOrders.getId());//商品订单号
        payRecord.setOrderName(xcOrders.getOrderName());
        payRecord.setTotalPrice(xcOrders.getTotalPrice());
        payRecord.setCurrency("CNY");
        payRecord.setCreateTime(DateUtils.getNowDate());
        payRecord.setStatus("NOTPAY");//未支付
//        payRecord.setUserId(xcOrders.getUserId());
        int i = payRecordMapper.insertXcPayRecord(payRecord);
        if (i <= 0) {
            //插入记录失败
            throw new OrderCreationFailedException();
        }

        return payRecord;

    }


    /**
     * 修改订单信息
     *
     * @param xcOrders 订单信息
     * @return 结果
     */
    @Override
    public int updateXcOrders(XcOrders xcOrders) {
        xcOrders.setUpdateTime(DateUtils.getNowDate());
        return xcOrdersMapper.updateXcOrders(xcOrders);
    }

    /**
     * 批量删除订单信息
     *
     * @param ids 需要删除的订单信息主键
     * @return 结果
     */
    @Override
    public int deleteXcOrdersByIds(String[] ids) {
        return xcOrdersMapper.deleteXcOrdersByIds(ids);
    }

    /**
     * 删除订单信息信息
     *
     * @param id 订单信息主键
     * @return 结果
     */
    @Override
    public int deleteXcOrdersById(String id) {
        return xcOrdersMapper.deleteXcOrdersById(id);
    }

    @Override
    @Transactional
    public String cancelOrder(String id) throws WxPayException {
        XcPayRecord payRecord = payRecordService.selectXcPayRecordById(Long.valueOf(id));
        if (payRecord == null || StringUtils.isEmpty(payRecord.getOrderId())) {
            throw new ServiceException("订单不存在");
        }
        //调用微信支付关闭订单
        wxService.closeOrderV3(payRecord.getOrderId());
        //更新商户端的订单状态
        XcPayRecord xcPayRecord = new XcPayRecord();
        xcPayRecord.setId(id);
        xcPayRecord.setStatus(OrderConstants.TradeState.CLOSED);
        payRecordMapper.updateXcPayRecord(xcPayRecord);
        return "取消成功！";
    }

    @Transactional
    @Override
    public AjaxResult refundOrder(Map<String, Object> order) {
        String id = (String) order.get("id");
        Integer refund = (Integer) order.get("refund");
        String reason = (String) order.get("reason");

        XcPayRecord payRecord = payRecordService.selectXcPayRecordById(Long.valueOf(id));
        if (payRecord == null || StringUtils.isEmpty(payRecord.getOrderId())) {
            throw new ServiceException("\n订单不存在");
        }
        //创建退款单记录
        log.info("\n创建退款单记录=========>order:{}", id);
        XcRefundOrder refundOrder = createRefundOrder(payRecord, refund, reason);

        //调用微信支付关闭订单
        //组装请求参数
        WxPayRefundV3Request request = new WxPayRefundV3Request();
        // transaction_id 微信支付订单号
        //原支付交易对应的微信订单号
        //示例值：1217752501201407033233368018
        request.setTransactionId(refundOrder.getTransactionId());
        //outRefundNo 商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@ ，同一退款单号多次请求只退一笔。
        request.setOutRefundNo(refundOrder.getOutRefundNo()); //退款单号 必填
        //  out_refund_no: 原支付交易对应的商户订单号
        request.setOutTradeNo(payRecord.getPayNo());
        //原因
        request.setReason(request.getReason());
        //退款结果回调url
//        request.setNotifyUrl("");
        //金额
        WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
        amount.setRefund(refund);
        amount.setTotal(refundOrder.getRefund());
        amount.setCurrency("CNY");
        request.setAmount(amount);
        request.setNotifyUrl(properties.getRefundNotifyUrl());
        log.info("\n调用退款API==========> 请求参数：{}", request);
        //先申请看是否成功！
        try {
            WxPayRefundV3Result refundV3Result = wxService.refundV3(request);
            log.info("\n退款申请返回==========> {}", refundV3Result);
            //更新订单 退款单
            uploadRefundOrder(refundV3Result, refundOrder);
            //支付记录 转入退款
            XcPayRecord xcPayRecord = new XcPayRecord();
            xcPayRecord.setId(id);
            xcPayRecord.setStatus(OrderConstants.TradeState.REFUND);
            //退款记录
            payRecordService.updateXcPayRecord(xcPayRecord);
            //订单状态
            updateXcOrdersByOrderId(payRecord.getOrderId(), OrderConstants.TradeState.REFUND);
            log.info("\n更新退款记录、更新支付记录、更新订单状态==============>");
            return AjaxResult.success(refundV3Result);
        } catch (WxPayException e) {
            //退款异常
            log.info("\n退款异常==============>\n异常代码：{}\n 异常描述{}", e.getErrCode(), e.getErrCodeDes());
            refundOrder.setStatus(OrderConstants.RefundState.ABNORMAL);
            refundOrder.setRemark(e.getErrCode() + ":" + e.getErrCodeDes());
            refundOrder.setContentReturn(gson.toJson(e));
            refundOrderService.updateXcRefundOrder(refundOrder);
            return AjaxResult.error(e.getCustomErrorMsg(), e.getErrCode());
        } catch (ParseException e) {
            return AjaxResult.error(e.getMessage());
        }
    }


    @Transactional
    @Override
    public void manualConf(String orderId, String amount, String currency,String remark) {
        if (StringUtils.isEmpty(StringUtils.trim(orderId))) {
            throw new ServiceException("订单不存在！");
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        XcOrders orders = selectXcOrdersById(orderId);
        if (orders == null) {
            throw new ServiceException("订单不存在！");
        }
        if (!OrderConstants.TradeState.NOTPAY.equals(orders.getStatus())) {
            throw new ServiceException("订单不满足核销销情况！");
        }
        String time = DateUtils.getTime();
        XcPayRecord payRecord = new XcPayRecord();
        //订单
        payRecord.setOrderId(orderId);
        payRecord.setStudentId(orders.getOther1());
        payRecord.setTotalPrice(orders.getTotalPrice());
        payRecord.setTotalAmount(orders.getTotalPrice());
        payRecord.setCurrency(currency);
        payRecord.setPayerTotal(amount);
        payRecord.setPayerCurrency(currency);
        //时间
        payRecord.setCreateTime(DateUtils.getNowDate());
        payRecord.setPaySuccessTime(time);
        payRecord.setPayNo(String.valueOf(idWorker.nextId()));
        //状态
        payRecord.setStatus("HEXIAO");
        //核销人员
        payRecord.setOutTradeNo("HX-" + time);
        payRecord.setCreateBy(String.valueOf("[" + loginUser.getUserId() + ":" + loginUser.getUsername() + "]"));
        payRecord.setContent("由用户[" + loginUser.getUserId() + "-" + loginUser.getUsername() + "-" + loginUser.getUser().getNickName() + "] 进行核销订单！");
        payRecord.setRemark(remark);
        //修改订单状态
        updateXcOrdersByOrderId(orderId, OrderConstants.TradeState.SUCCESS,remark);
        //记录核销日志
        payRecordService.save(payRecord);

    }

    /**
     * 是否 有人进行了缴费
     *
     * @param scheduleId
     * @return
     */
    @Override
    public boolean hasStudentPaid(String scheduleId) {
        XcOrders xcOrders = new XcOrders();
        xcOrders.setOther1(scheduleId);
        List<XcOrders> listOrder = ordersService.selectXcOrdersList(xcOrders);
        // 如果没有订单，直接返回false
        if (listOrder == null || listOrder.isEmpty()) {
            return false;
        }
        // 提取订单ID列表
        List<String> orderIds = listOrder.stream().map(XcOrders::getId).collect(Collectors.toList());

        // 创建支付记录查询条件
        LambdaQueryWrapper<XcPayRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(XcPayRecord::getOrderId, orderIds);
        // 查询是否存在支付记录
        long count = payRecordMapper.selectCount(queryWrapper);

        // 如果存在支付记录，返回true，否则返回false
        return count > 0;
    }

    XcRefundOrder createRefundOrder(XcPayRecord payOrder, Integer refund, String reason) {
        //1.根据订单号获取订单 payOrder

        //2.根据订单号生成退款订单
        XcRefundOrder refundOrder = new XcRefundOrder();
        //生成支付交易流水号 传给第三方支付平台
        long refundNo = idWorker.nextId();
        refundOrder.setOrderId(payOrder.getOrderId());//商品订单号
        refundOrder.setOutRefundNo(String.valueOf(refundNo));//商户退款单号  *
        refundOrder.setTransactionId(payOrder.getOutPayNo());//微信支付单号
        refundOrder.setRefund(refund);//退款金额  *
        refundOrder.setReason(reason);//退款原因  *
        refundOrder.setPayerTotal(Integer.valueOf(payOrder.getTotalPrice())); //原订单金额 *
        refundOrder.setCurrency("CNY"); //退款币种
        refundOrder.setCreateTime(DateUtils.getNowDate());
        refundOrder.setStatus(OrderConstants.RefundState.PROCESSING);//转入退款
        refundOrder.setRefundId(payOrder.getTransactionId());//微信支付订单号

        //3.保存退款订单
        int i = refundOrderService.insertXcRefundOrder(refundOrder);
        if (i <= 0) {
            //插入记录失败
            throw new OrderCreationFailedException();
        }

        return refundOrder;
    }

    void uploadRefundOrder(WxPayRefundV3Result v3Result, XcRefundOrder refundOrder) throws ParseException {
        refundOrder.setRefundId(v3Result.getRefundId()); //微信支付退款单号
        refundOrder.setOutRefundNo(v3Result.getOutRefundNo()); //商户退款单号
        refundOrder.setTransactionId(v3Result.getTransactionId()); //微信支付订单号
        refundOrder.setOrderId(v3Result.getOutTradeNo()); //商户订单号
        refundOrder.setChannel(v3Result.getChannel()); //退款渠道
        refundOrder.setUserReceivedAccount(v3Result.getUserReceivedAccount()); //退款入账账户
        //退款时间
        try {
            String createTime = v3Result.getCreateTime();
            SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
            Date parse = inputFormat.parse(createTime);
            refundOrder.setCreateTime(parse);//退款创建时间
        } catch (ParseException e) {
            Date nowDate = DateUtils.getNowDate();
            refundOrder.setCreateTime(nowDate);//退款创建时间
        }
        refundOrder.setStatus(v3Result.getStatus());//退款状态
        WxPayRefundV3Result.Amount amountR = v3Result.getAmount();
        refundOrder.setTotal(String.valueOf(amountR.getTotal()));
        refundOrder.setRefund(amountR.getRefund());
        refundOrder.setPayerRefund(amountR.getPayerRefund());
        refundOrder.setSettlementTota(amountR.getSettlementRefund());
        refundOrder.setSettlementTota(amountR.getSettlementTotal());
        refundOrder.setDiscountRefund(amountR.getDiscountRefund());
        refundOrder.setCurrency(amountR.getCurrency());
//            refundOrder.setRefundFee();
        refundOrder.setContentReturn(gson.toJson(v3Result));
        refundOrderService.updateXcRefundOrder(refundOrder);
    }

    void updateXcOrdersByOrderId(String id, String status) {
        XcOrders orders = new XcOrders();
        orders.setId(id);
        orders.setUpdateTime(DateUtils.getNowDate());
        orders.setStatus(status);
        xcOrdersMapper.updateXcOrders(orders);
    }

    void updateXcOrdersByOrderId(String id, String status,String remark) {
        XcOrders orders = new XcOrders();
        orders.setId(id);
        orders.setUpdateTime(DateUtils.getNowDate());
        orders.setStatus(status);
        orders.setRemark(remark);
        xcOrdersMapper.updateXcOrders(orders);
    }
}
